--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
scripts/check-format-tokens.mjs 65ca9ddc823fcc2c8f8653c553b944b85db0875f (65ca9ddc) Text, 4.25 KB
T8b949e#!/usr/bin/env node
T8b949e// SPDX-License-Identifier: 0BSD
T8b949e/**
* Fail when formatDate / meshDate(...).format patterns use unsupported tokens.
* Keeps meshchatx/src/frontend/libs/datetime.js as the single source of truth.
*/
Tff7b72import Te6edf3fs Tff7b72from Ta5d6ff"node:fs"Tb4b4b4;
Tff7b72import Te6edf3path Tff7b72from Ta5d6ff"node:path"Tb4b4b4;
Tff7b72import Te6edf3process Tff7b72from Ta5d6ff"node:process"Tb4b4b4;
Tff7b72import Tb4b4b4{ Te6edf3fileURLToPath Tb4b4b4} Tff7b72from Ta5d6ff"node:url"Tb4b4b4;
Tff7b72const Te6edf3__dirname Tff7b72= Te6edf3pathTb4b4b4.Te6edf3dirnameTb4b4b4(Te6edf3fileURLToPathTb4b4b4(Tff7b72importTb4b4b4.Te6edf3metaTb4b4b4.Te6edf3urlTb4b4b4)Tb4b4b4)Tb4b4b4;
Tff7b72const Te6edf3ROOT Tff7b72= Te6edf3pathTb4b4b4.Te6edf3resolveTb4b4b4(Te6edf3__dirnameTb4b4b4, Ta5d6ff".."Tb4b4b4)Tb4b4b4;
Tff7b72const Te6edf3FRONTEND_ROOT Tff7b72= Te6edf3pathTb4b4b4.Te6edf3joinTb4b4b4(Te6edf3ROOTTb4b4b4, Ta5d6ff"meshchatx"Tb4b4b4, Ta5d6ff"src"Tb4b4b4, Ta5d6ff"frontend"Tb4b4b4)Tb4b4b4;
Tff7b72const Te6edf3DATETIME_LIB Tff7b72= Te6edf3pathTb4b4b4.Te6edf3joinTb4b4b4(Te6edf3FRONTEND_ROOTTb4b4b4, Ta5d6ff"libs"Tb4b4b4, Ta5d6ff"datetime.js"Tb4b4b4)Tb4b4b4;
Tff7b72const Te6edf3SCAN_EXTENSIONS Tff7b72= Tff7b72new Tffa657SetTb4b4b4(Tb4b4b4[Ta5d6ff".js"Tb4b4b4, Ta5d6ff".mjs"Tb4b4b4, Ta5d6ff".cjs"Tb4b4b4, Ta5d6ff".vue"Tb4b4b4, Ta5d6ff".ts"Tb4b4b4, Ta5d6ff".tsx"Tb4b4b4]Tb4b4b4)Tb4b4b4;
T8b949e/** @type {RegExp[]} */
Tff7b72const Te6edf3PATTERN_EXTRACTORS Tff7b72= Tb4b4b4[
Ta5d6ff/\bformatDate\s*\(\s*[^,]+?\s*,\s*(['"`])([^'"`]+?)\1/gTb4b4b4,
Ta5d6ff/\bmeshDate\s*\(\s*[^)]*?\)\s*\.\s*format\s*\(\s*(['"`])([^'"`]+?)\1/gTb4b4b4,
Ta5d6ff/\bdayjs\s*\(\s*[^)]*?\)\s*\.\s*format\s*\(\s*(['"`])([^'"`]+?)\1/gTb4b4b4,
Tb4b4b4]Tb4b4b4;
T8b949e/**
* @param {string} source
* @returns {string[]}
*/
Tff7b72function Te6edf3readSupportedTokensTb4b4b4(Te6edf3sourceTb4b4b4) Tb4b4b4{
Tff7b72const Te6edf3match Tff7b72= Te6edf3sourceTb4b4b4.Te6edf3matchTb4b4b4(Ta5d6ff/export const SUPPORTED_FORMAT_TOKENS = Object\.freeze\(\[([\s\S]*?)\]\)/Tb4b4b4)Tb4b4b4;
Tff7b72if Tb4b4b4(Tff7b72!Te6edf3matchTb4b4b4) Tb4b4b4{
Tff7b72return Tb4b4b4[Tb4b4b4]Tb4b4b4;
Tb4b4b4}
Tff7b72return Tffa657ArrayTb4b4b4.Tff7b72fromTb4b4b4(Te6edf3matchTb4b4b4[T79c0ff1Tb4b4b4]Tb4b4b4.Te6edf3matchAllTb4b4b4(Ta5d6ff/"([^"]+)"/gTb4b4b4)Tb4b4b4, Tb4b4b4(Te6edf3mTb4b4b4) Tb4b4b4=> Te6edf3mTb4b4b4[T79c0ff1Tb4b4b4]Tb4b4b4)Tb4b4b4;
Tb4b4b4}
T8b949e/**
* @param {string} dir
* @param {(filePath: string) => void} visit
*/
Tff7b72function Te6edf3walkTb4b4b4(Te6edf3dirTb4b4b4, Te6edf3visitTb4b4b4) Tb4b4b4{
Tff7b72for Tb4b4b4(Tff7b72const Te6edf3entry Tff7b72of Te6edf3fsTb4b4b4.Te6edf3readdirSyncTb4b4b4(Te6edf3dirTb4b4b4, Tb4b4b4{ Te6edf3withFileTypesTff7b72: Tff7b72true Tb4b4b4}Tb4b4b4)Tb4b4b4) Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3entryTb4b4b4.Te6edf3name Tff7b72=== Ta5d6ff"node_modules" Tff7b72|| Te6edf3entryTb4b4b4.Te6edf3name Tff7b72=== Ta5d6ff"public" Tff7b72|| Te6edf3entryTb4b4b4.Te6edf3nameTb4b4b4.Te6edf3startsWithTb4b4b4(Ta5d6ff"."Tb4b4b4)Tb4b4b4) Tb4b4b4{
Tff7b72continueTb4b4b4;
Tb4b4b4}
Tff7b72const Te6edf3full Tff7b72= Te6edf3pathTb4b4b4.Te6edf3joinTb4b4b4(Te6edf3dirTb4b4b4, Te6edf3entryTb4b4b4.Te6edf3nameTb4b4b4)Tb4b4b4;
Tff7b72if Tb4b4b4(Te6edf3entryTb4b4b4.Te6edf3isDirectoryTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Te6edf3walkTb4b4b4(Te6edf3fullTb4b4b4, Te6edf3visitTb4b4b4)Tb4b4b4;
Tff7b72continueTb4b4b4;
Tb4b4b4}
Tff7b72if Tb4b4b4(Te6edf3SCAN_EXTENSIONSTb4b4b4.Te6edf3hasTb4b4b4(Te6edf3pathTb4b4b4.Te6edf3extnameTb4b4b4(Te6edf3entryTb4b4b4.Te6edf3nameTb4b4b4)Tb4b4b4)Tb4b4b4) Tb4b4b4{
Te6edf3visitTb4b4b4(Te6edf3fullTb4b4b4)Tb4b4b4;
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
T8b949e/**
* @param {string} pattern
* @param {ReadonlySet<string>} supported
* @returns {string[]}
*/
Tff7b72function Te6edf3unsupportedTokensTb4b4b4(Te6edf3patternTb4b4b4, Te6edf3supportedTb4b4b4) Tb4b4b4{
Tff7b72const Te6edf3tokenRe Tff7b72= Ta5d6ff/YYYY|MMM|MM|M|DD|D|HH|H|hh|h|mm|A|a|ss|SSS|Z|z|X|x|Do|ddd|dddd/gTb4b4b4;
Tff7b72const Te6edf3found Tff7b72= Te6edf3patternTb4b4b4.Te6edf3matchTb4b4b4(Te6edf3tokenReTb4b4b4) Tff7b72|| Tb4b4b4[Tb4b4b4]Tb4b4b4;
Tff7b72return Tb4b4b4[Tb4b4b4...Tff7b72new Tffa657SetTb4b4b4(Te6edf3foundTb4b4b4.Te6edf3filterTb4b4b4(Tb4b4b4(Te6edf3tokenTb4b4b4) Tb4b4b4=> Tff7b72!Te6edf3supportedTb4b4b4.Te6edf3hasTb4b4b4(Te6edf3tokenTb4b4b4)Tb4b4b4)Tb4b4b4)Tb4b4b4]Tb4b4b4;
Tb4b4b4}
Tff7b72function Te6edf3mainTb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72const Te6edf3datetimeSource Tff7b72= Te6edf3fsTb4b4b4.Te6edf3readFileSyncTb4b4b4(Te6edf3DATETIME_LIBTb4b4b4, Ta5d6ff"utf8"Tb4b4b4)Tb4b4b4;
Tff7b72const Te6edf3supportedList Tff7b72= Te6edf3readSupportedTokensTb4b4b4(Te6edf3datetimeSourceTb4b4b4)Tb4b4b4;
Tff7b72if Tb4b4b4(Te6edf3supportedListTb4b4b4.Te6edf3length Tff7b72=== T79c0ff0Tb4b4b4) Tb4b4b4{
Te6edf3consoleTb4b4b4.Te6edf3errorTb4b4b4(Ta5d6ff"check-format-tokens: SUPPORTED_FORMAT_TOKENS missing from datetime.js"Tb4b4b4)Tb4b4b4;
Te6edf3processTb4b4b4.Te6edf3exitTb4b4b4(T79c0ff1Tb4b4b4)Tb4b4b4;
Tb4b4b4}
Tff7b72const Te6edf3supported Tff7b72= Tff7b72new Tffa657SetTb4b4b4(Te6edf3supportedListTb4b4b4)Tb4b4b4;
T8b949e/** @type {{ file: string, pattern: string, tokens: string[] }[]} */
Tff7b72const Te6edf3failures Tff7b72= Tb4b4b4[Tb4b4b4]Tb4b4b4;
T8b949e/** @type {{ file: string, pattern: string }[]} */
Tff7b72const Te6edf3dayjsHits Tff7b72= Tb4b4b4[Tb4b4b4]Tb4b4b4;
Te6edf3walkTb4b4b4(Te6edf3FRONTEND_ROOTTb4b4b4, Tb4b4b4(Te6edf3filePathTb4b4b4) Tb4b4b4=> Tb4b4b4{
Tff7b72const Te6edf3rel Tff7b72= Te6edf3pathTb4b4b4.Te6edf3relativeTb4b4b4(Te6edf3ROOTTb4b4b4, Te6edf3filePathTb4b4b4)Tb4b4b4.Te6edf3replaceTb4b4b4(Ta5d6ff/\\/gTb4b4b4, Ta5d6ff"/"Tb4b4b4)Tb4b4b4;
Tff7b72if Tb4b4b4(Te6edf3relTb4b4b4.Te6edf3endsWithTb4b4b4(Ta5d6ff"libs/datetime.js"Tb4b4b4)Tb4b4b4) Tb4b4b4{
Tff7b72returnTb4b4b4;
Tb4b4b4}
Tff7b72const Te6edf3text Tff7b72= Te6edf3fsTb4b4b4.Te6edf3readFileSyncTb4b4b4(Te6edf3filePathTb4b4b4, Ta5d6ff"utf8"Tb4b4b4)Tb4b4b4;
Tff7b72for Tb4b4b4(Tff7b72const Te6edf3extractor Tff7b72of Te6edf3PATTERN_EXTRACTORSTb4b4b4) Tb4b4b4{
Te6edf3extractorTb4b4b4.Te6edf3lastIndex Tff7b72= T79c0ff0Tb4b4b4;
Tff7b72let Te6edf3matchTb4b4b4;
Tff7b72while Tb4b4b4(Tb4b4b4(Te6edf3match Tff7b72= Te6edf3extractorTb4b4b4.Te6edf3execTb4b4b4(Te6edf3textTb4b4b4)Tb4b4b4) Tff7b72!== Tff7b72nullTb4b4b4) Tb4b4b4{
Tff7b72const Te6edf3pattern Tff7b72= Te6edf3matchTb4b4b4[T79c0ff2Tb4b4b4]Tb4b4b4;
Tff7b72if Tb4b4b4(Te6edf3extractorTb4b4b4.Te6edf3sourceTb4b4b4.Te6edf3includesTb4b4b4(Ta5d6ff"dayjs"Tb4b4b4)Tb4b4b4) Tb4b4b4{
Te6edf3dayjsHitsTb4b4b4.Te6edf3pushTb4b4b4(Tb4b4b4{ Te6edf3fileTff7b72: Te6edf3relTb4b4b4, Te6edf3pattern Tb4b4b4}Tb4b4b4)Tb4b4b4;
Tb4b4b4}
Tff7b72const Te6edf3bad Tff7b72= Te6edf3unsupportedTokensTb4b4b4(Te6edf3patternTb4b4b4, Te6edf3supportedTb4b4b4)Tb4b4b4;
Tff7b72if Tb4b4b4(Te6edf3badTb4b4b4.Te6edf3length Tff7b72> T79c0ff0Tb4b4b4) Tb4b4b4{
Te6edf3failuresTb4b4b4.Te6edf3pushTb4b4b4(Tb4b4b4{ Te6edf3fileTff7b72: Te6edf3relTb4b4b4, Te6edf3patternTb4b4b4, Te6edf3tokensTff7b72: Te6edf3bad Tb4b4b4}Tb4b4b4)Tb4b4b4;
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}Tb4b4b4)Tb4b4b4;
Tff7b72if Tb4b4b4(Te6edf3dayjsHitsTb4b4b4.Te6edf3length Tff7b72> T79c0ff0Tb4b4b4) Tb4b4b4{
Te6edf3consoleTb4b4b4.Te6edf3errorTb4b4b4(Ta5d6ff"check-format-tokens: dayjs(...).format still used in frontend sources:"Tb4b4b4)Tb4b4b4;
Tff7b72for Tb4b4b4(Tff7b72const Te6edf3hit Tff7b72of Te6edf3dayjsHitsTb4b4b4) Tb4b4b4{
Te6edf3consoleTb4b4b4.Te6edf3errorTb4b4b4(Ta5d6ff`Ta5d6ff Tffd700${Te6edf3hitTb4b4b4.Te6edf3fileTffd700}Ta5d6ff: Tffd700${Te6edf3hitTb4b4b4.Te6edf3patternTffd700}Ta5d6ff`Tb4b4b4)Tb4b4b4;
Tb4b4b4}
Te6edf3processTb4b4b4.Te6edf3exitTb4b4b4(T79c0ff1Tb4b4b4)Tb4b4b4;
Tb4b4b4}
Tff7b72if Tb4b4b4(Te6edf3failuresTb4b4b4.Te6edf3length Tff7b72> T79c0ff0Tb4b4b4) Tb4b4b4{
Te6edf3consoleTb4b4b4.Te6edf3errorTb4b4b4(Ta5d6ff"check-format-tokens: unsupported format tokens:"Tb4b4b4)Tb4b4b4;
Tff7b72for Tb4b4b4(Tff7b72const Te6edf3failure Tff7b72of Te6edf3failuresTb4b4b4) Tb4b4b4{
Te6edf3consoleTb4b4b4.Te6edf3errorTb4b4b4(Ta5d6ff`Ta5d6ff Tffd700${Te6edf3failureTb4b4b4.Te6edf3fileTffd700}Ta5d6ff: "Tffd700${Te6edf3failureTb4b4b4.Te6edf3patternTffd700}Ta5d6ff" -> Tffd700${Te6edf3failureTb4b4b4.Te6edf3tokensTb4b4b4.Te6edf3joinTb4b4b4(Ta5d6ff", "Tb4b4b4)Tffd700}Ta5d6ff`Tb4b4b4)Tb4b4b4;
Tb4b4b4}
Te6edf3consoleTb4b4b4.Te6edf3errorTb4b4b4(Ta5d6ff`Ta5d6ffSupported tokens: Tffd700${Te6edf3supportedListTb4b4b4.Te6edf3joinTb4b4b4(Ta5d6ff" "Tb4b4b4)Tffd700}Ta5d6ff`Tb4b4b4)Tb4b4b4;
Te6edf3processTb4b4b4.Te6edf3exitTb4b4b4(T79c0ff1Tb4b4b4)Tb4b4b4;
Tb4b4b4}
Te6edf3consoleTb4b4b4.Te6edf3logTb4b4b4(Ta5d6ff"check-format-tokens: ok"Tb4b4b4)Tb4b4b4;
Tb4b4b4}
Te6edf3mainTb4b4b4(Tb4b4b4)Tb4b4b4;
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────